
import {
    Blob,
    btoa,
    createImageBitmap,
    CSSStyleDeclaration,
    performance,
    document,
    DOMParser,
    EventTarget,
    fetch,
    Headers,
    HTMLCanvasElement,
	Image,
    HTMLImageElement,
    ImageBitmap,
    location,
    navigator,
    Request,
    requestAnimationFrame,
    cancelAnimationFrame,
    Response,
    URL,
    window,
    self,
    WebAssembly,
    Worker,
    XMLHttpRequest,
	ImageData,
	TextDecoder,
    core
    } from 'dhtml-weixin';
/**
 * Ascii generation is based on https://github.com/hassadee/jsascii/blob/master/jsascii.js
 *
 * 16 April 2012 - @blurspline
 */

class AsciiEffect {

    constructor(renderer, charSet = ' .:-=+*#%@', options = {}) {

        // ' .,:;=|iI+hHOE#`$';
        // darker bolder character set from https://github.com/saw/Canvas-ASCII-Art/
        // ' .\'`^",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$'.split('');

        // Some ASCII settings

        const fResolution = options['resolution'] || 0.15; // Higher for more details
        const iScale = options['scale'] || 1;
        const bColor = options['color'] || false; // nice but slows down rendering!
        const bAlpha = options['alpha'] || false; // Transparency
        const bBlock = options['block'] || false; // blocked characters. like good O dos
        const bInvert = options['invert'] || false; // black is white, white is black
        const strResolution = options['strResolution'] || 'low';

        let width, height;

        const domElement = document.createElement('div');
        domElement.style.cursor = 'default';

        const oAscii = document.createElement('table');
        oAscii.id = "oAscii"
        domElement.appendChild(oAscii);

        let iWidth, iHeight;
        let oImg;

        this.setSize = function (w, h) {

            width = w;
            height = h;

            renderer.setSize(w, h);

            initAsciiSize();

        };


        this.render =  function (scene, camera) {

            renderer.render(scene, camera);
             asciifyImage(oAscii).then();

        };

        this.domElement = domElement;


        // Throw in ascii library from https://github.com/hassadee/jsascii/blob/master/jsascii.js (MIT License)

        function initAsciiSize() {

            iWidth = Math.round(width * fResolution);
            iHeight = Math.round(height * fResolution);

            oCanvas.width = iWidth;
            oCanvas.height = iHeight;
            // oCanvas.style.display = "none";
            // oCanvas.style.width = iWidth;
            // oCanvas.style.height = iHeight;

            oImg = renderer.domElement;

            /*if ( oImg.style.backgroundColor ) {

            	oAscii.rows[ 0 ].cells[ 0 ].style.backgroundColor = oImg.style.backgroundColor;
            	oAscii.rows[ 0 ].cells[ 0 ].style.color = oImg.style.color;

            }*/

            oAscii.cellSpacing = 0;
            oAscii.cellPadding = 0;

            const oStyle = oAscii.style;
            oStyle.display = 'inline';
            oStyle.width = Math.round(iWidth / fResolution * iScale) + 'px';
            oStyle.height = Math.round(iHeight / fResolution * iScale) + 'px';
            oStyle.whiteSpace = 'pre';
            oStyle.margin = '0px';
            oStyle.padding = '0px';
            oStyle.letterSpacing = fLetterSpacing + 'px';
            oStyle.fontFamily = strFont;
            oStyle.fontSize = fFontSize + 'px';
            oStyle.lineHeight = fLineHeight + 'px';
            oStyle.textAlign = 'left';
            oStyle.textDecoration = 'none';

        }


        const aDefaultCharList = (' .,:;i1tfLCG08@').split('');
        const aDefaultColorCharList = (' CGO08@').split('');
        const strFont = 'courier new, monospace';

        const oCanvasImg = renderer.domElement;

        const oCanvas = document.createElement('canvas',"2d");
        if (!oCanvas.getContext) {

            return;

        }
        const oCtx = oCanvas.getContext('2d');
        if (!oCtx.getImageData) {

            return;

        }

        let aCharList = (bColor ? aDefaultColorCharList : aDefaultCharList);

        if (charSet) aCharList = charSet;

        // Setup dom

        const fFontSize = (2 / fResolution) * iScale;
        const fLineHeight = (2 / fResolution) * iScale;

        // adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width.

        let fLetterSpacing = 0;

        if (strResolution == 'low') {

            switch (iScale) {

                case 1:
                    fLetterSpacing = -1;
                    break;
                case 2:
                case 3:
                    fLetterSpacing = -2.1;
                    break;
                case 4:
                    fLetterSpacing = -3.1;
                    break;
                case 5:
                    fLetterSpacing = -4.15;
                    break;

            }

        }

        if (strResolution == 'medium') {

            switch (iScale) {

                case 1:
                    fLetterSpacing = 0;
                    break;
                case 2:
                    fLetterSpacing = -1;
                    break;
                case 3:
                    fLetterSpacing = -1.04;
                    break;
                case 4:
                case 5:
                    fLetterSpacing = -2.1;
                    break;

            }

        }

        if (strResolution == 'high') {

            switch (iScale) {

                case 1:
                case 2:
                    fLetterSpacing = 0;
                    break;
                case 3:
                case 4:
                case 5:
                    fLetterSpacing = -1;
                    break;

            }

        }


        // can't get a span or div to flow like an img element, but a table works?
        async function asciifyImage(oAscii) {      
            //   console.error(oAscii)
            oCtx.clearRect(0, 0, iWidth, iHeight);
          
            oCtx.drawImage(await core.Canvas.toImage(oCanvas,oCanvasImg), 0, 0, iWidth, iHeight);
            const oImgData = oCtx.getImageData(0, 0, iWidth, iHeight).data;
   

            // Coloring loop starts now
            let strChars = '';

            // console.time('rendering');

            for (let y = 0; y < iHeight; y += 2) {

                for (let x = 0; x < iWidth; x++) {

                    const iOffset = (y * iWidth + x) * 4;

                    const iRed = oImgData[iOffset];
                    const iGreen = oImgData[iOffset + 1];
                    const iBlue = oImgData[iOffset + 2];
                    const iAlpha = oImgData[iOffset + 3];
                    let iCharIdx;

                    let fBrightness;

                    fBrightness = (0.3 * iRed + 0.59 * iGreen + 0.11 * iBlue) / 255;
                    // fBrightness = (0.3*iRed + 0.5*iGreen + 0.3*iBlue) / 255;

                    if (iAlpha == 0) {

                        // should calculate alpha instead, but quick hack :)
                        //fBrightness *= (iAlpha / 255);
                        fBrightness = 1;

                    }

                    iCharIdx = Math.floor((1 - fBrightness) * (aCharList.length - 1));

                    if (bInvert) {

                        iCharIdx = aCharList.length - iCharIdx - 1;

                    }

                    // good for debugging
                    //fBrightness = Math.floor(fBrightness * 10);
                    //strThisChar = fBrightness;

                    let strThisChar = aCharList[iCharIdx];

                    if (strThisChar === undefined || strThisChar == ' ')
                        strThisChar = '&nbsp;';

                    if (bColor) {

                        strChars += '<span style=\'' +
                            'color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');' +
                            (bBlock ? 'background-color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');' : '') +
                            (bAlpha ? 'opacity:' + (iAlpha / 255) + ';' : '') +
                            '\'>' + strThisChar + '</span>';

                    } else {

                        strChars += strThisChar;

                    }

                }

                strChars += '<br/>';

            }
            oAscii.innerHTML =strChars ;

        
            // console.timeEnd('rendering');

            // return oAscii;

        }

    }

}

export {
    AsciiEffect
};